<template>
  <div>
    <div id="container" ref="container">
      <div v-for="(card, index) in cards" :key="index" class="card" :style="cardStyle(card)"
        @mouseover="handlemouseover" @mouseout="handlemouseout"></div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      numCards: 10, // 渲染的卡片数量
      maxCardSize: 50, // 卡片在椭圆底部时的最大尺寸（实现近大远小）
      minCardSize: 40, // 卡片在椭圆顶部时的最小尺寸（实现近大远小）
      ellipseWidth: 400, // 椭圆宽度示例（调整椭圆的宽度和高度）
      ellipseHeight: 150, // 椭圆高度示例
      centerX: 0,
      centerY: 0,
      speed: 0.005, // 根据需要调整速度
      cards: [], // 存储卡片信息的数组
      animationId: null // 存储 requestAnimationFrame ID 的变量
    };
  },
  mounted() {
    // 计算中心点位
    this.centerX = this.$refs.container.offsetWidth / 2;
    this.centerY = this.$refs.container.offsetHeight / 2;
    console.log('中心点的位置', this.centerX, this.centerY);
    // 创建10个卡片
    this.createCards();
    this.animateCards();
  },
  methods: {
    /**
     * 创建卡片并设置初始角度
     * 
     * 此函数用于创建指定数量的卡片，并为每张卡片设置一个初始角度
     * 角度的设置是根据卡片在数组中的位置来决定的，这样可以确保卡片均匀分布在一个圆周上
     */
    createCards() {
      // angle初始角度值
      for (let i = 0; i < this.numCards; i++) {
        this.cards.push({
          angle: (i / this.numCards) * Math.PI * 2
        });
      }
    },
    /**
     * 动画函数，用于动态调整卡片的位置和大小，使卡片在椭圆形路径上移动
     * 此函数通过遍历每张卡片，根据其当前角度计算在椭圆上的位置，并更新卡片的大小
     * 卡片的角度会随时间递增，以实现持续的动画效果
     */
    animateCards() {
      // 遍历每张卡片，调整其位置和大小
      this.cards.forEach((card, index) => {
        // 获取卡片当前的角度
        const angle = card.angle;
        // 根据角度计算卡片在x轴上的位置，使其沿椭圆路径移动
        const x = this.centerX + this.ellipseWidth * Math.cos(angle);
        // 根据角度计算卡片在y轴上的位置，使其沿椭圆路径移动
        const y = this.centerY + this.ellipseHeight * Math.sin(angle);
        // 打印卡片的位置，用于调试
        // console.log('(x,y)', x, y);
        // 根据卡片在y轴上的位置动态调整卡片的大小，使卡片大小随位置变化
        let size = this.minCardSize + (this.maxCardSize - this.minCardSize) * ((y - this.centerY + this.ellipseHeight) / (2 * this.ellipseHeight));

        // 更新卡片的角度，以在下一次动画帧中改变位置
        card.angle += this.speed;
        // 如果角度超过一圈（2π），则重置角度，实现循环动画效果
        if (card.angle > Math.PI * 2) {
          card.angle -= Math.PI * 2;
        }

        // 更新卡片的信息，包括位置和大小
        // 使用Vue的$set方法来确保数据绑定的响应式
        this.$set(this.cards, index, {
          ...card,
          x,
          y,
          size
        });
      });

      // 请求下一帧动画，实现连续动画效果
      // 将animateCards函数自身作为参数传递，形成递归调用
      this.animationId = requestAnimationFrame(this.animateCards);
    },
    /**
     * 根据卡片的属性动态生成卡片的样式对象
     * 
     * 该函数用于根据卡片的当前位置(x, y坐标)、最大卡片尺寸(maxCardSize)以及卡片尺寸(size)来计算卡片的样式属性
     * 包括卡片的左上角位置(left, top)、宽度(width)和高度(height)
     * 
     * @param {Object} card - 当前卡片对象，包含卡片的x和y坐标、size属性以及maxCardSize属性
     * @returns {Object} 包含样式属性left、top、width和height的对象，用于设置卡片的样式
     */
    cardStyle(card) {
      return {
        left: card.x - this.maxCardSize / 2 + 'px', // 计算卡片左边界距离视图左边界的距离
        top: card.y - this.maxCardSize / 2 - 20 + 'px', // 计算卡片顶边距离视图顶边的距离，额外减去20px以调整位置
        width: card.size + 'px', // 设置卡片宽度
        height: card.size * 1.4 + 'px' // 根据宽度计算并设置卡片高度，比例为1.4
      };
    },
    stopAnimation() {
      cancelAnimationFrame(this.animationId);
    },
    startAnimation() {
      this.animateCards();
    },
    // 鼠标移入事件
    handlemouseover(val) {
      console.log('进入', val);
      this.stopAnimation()
    },
    // 鼠标移出事件
    handlemouseout(val) {
      console.log('移除', val);
      this.startAnimation()
    }
  },
  // 离开页面清理动画帧
  beforeDestroy() {
    cancelAnimationFrame(this.animationId);
  }
};
</script>

<style>
#container {
  width: 800px;
  height: 300px;
  position: relative;
  margin: 100px auto;
  border: 1px solid #ccc;
  border-radius: 50%;
  /* overflow: hidden; */
}

.card {
  position: absolute;
  width: 100px;
  height: 140px;
  background-color: #f00;
  border-radius: 8px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
  transform-origin: center bottom;
}
</style>
